﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using StyleMVVM.Configuration;
using StyleMVVM.Logging;
using StyleMVVM.Validation;
using StyleMVVM.DependencyInjection;
using StyleMVVM.View.Impl;

namespace StyleMVVM.View
{
	public class ValidationAdornerHelper : IPropertyValidationHandler
	{
		private static string supplemental = typeof(ValidationAdornerHelper).FullName;
		private WeakReference elementRef;
		private IValidationContext validationContext;
		private string propertyName;
		private Adorner adorner;

		public ValidationAdornerHelper(FrameworkElement element, object dataContext, string propertyName)
		{
			this.propertyName = propertyName;
			this.elementRef = new WeakReference(element);

			element.Loaded += LoadedHandler;
			element.IsVisibleChanged += ElementOnIsVisibleChanged;

			IValidationService validationService =
				Bootstrapper.Instance.Container.Locate<IValidationService>();

			if (validationService != null)
			{
				validationContext = validationService.GetValidationContext(dataContext);

				validationContext.RegisterPropertyValidationHandler(propertyName, this);

				ProcessPropertyValidation(validationContext.PropertyResults(propertyName));

				ProcessElementVisibility();
			}
			else
			{
				throw new Exception("Can't find IValidationService");
			}
		}

		private void ElementOnIsVisibleChanged(object sender,
		                                       DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
		{
			ProcessElementVisibility();
		}

		private void ProcessElementVisibility()
		{
			FrameworkElement frameworkElement = elementRef.Target as FrameworkElement;

			if (frameworkElement != null && adorner != null)
			{
				if (frameworkElement.IsVisible)
				{
					adorner.Visibility = Visibility.Visible;
				}
				else
				{
					adorner.Visibility = Visibility.Hidden;
				}
			}
		}

		public void Disconnect()
		{
			validationContext.UnregisterPropertyValidationHandler(propertyName, this);

			FrameworkElement view = elementRef.Target as FrameworkElement;

			AdornerLayer layer = AdornerLayer.GetAdornerLayer(view);

			if (layer != null && adorner != null)
			{
				layer.Remove(adorner);

				adorner = null;
			}
		}

		public static ValidationAdornerHelper GetHelper(DependencyObject obj)
		{
			return (ValidationAdornerHelper)obj.GetValue(HelperProperty);
		}

		public static void SetHelper(DependencyObject obj, ValidationAdornerHelper value)
		{
			obj.SetValue(HelperProperty, value);
		}

		// Using a DependencyProperty as the backing store for Helper.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty HelperProperty =
			DependencyProperty.RegisterAttached("Helper",
			                                    typeof(ValidationAdornerHelper),
			                                    typeof(ValidationAdornerHelper),
			                                    new PropertyMetadata(null));

		public void ProcessPropertyValidation(IEnumerable<IValidationRuleResultPart> ruleResultParts)
		{
			FrameworkElement view = elementRef.Target as FrameworkElement;
			ValidationState state = ValidationState.Valid;
			string validationMessage = null;

			if (view != null)
			{
				try
				{
					if (ruleResultParts != null)
					{
						foreach (IValidationRuleResultPart validationRuleResultPart in ruleResultParts)
						{
							if (validationRuleResultPart != null)
							{
								switch (validationRuleResultPart.State)
								{
									case ValidationState.ValidWithWarning:
										if (state == ValidationState.Valid)
										{
											state = ValidationState.ValidWithWarning;
										}
										validationMessage += validationRuleResultPart.Message + Environment.NewLine;
										break;
									case ValidationState.InvalidRequired:
										if (state != ValidationState.Invalid)
										{
											state = ValidationState.InvalidRequired;
										}
										validationMessage += validationRuleResultPart.Message + Environment.NewLine;
										break;
									case ValidationState.Invalid:
										state = ValidationState.Invalid;
										validationMessage += validationRuleResultPart.Message + Environment.NewLine;
										break;
								}
							}
						}
					}
					AdornerLayer layer = AdornerLayer.GetAdornerLayer(view);

					if (layer != null)
					{
						BorderAdorner currentBorderAdorner = adorner as BorderAdorner;

						switch (state)
						{
							case ValidationState.Valid:
								if (adorner != null)
								{
									layer.Remove(adorner);

									adorner = null;
								}
								break;
							case ValidationState.ValidWithWarning:
								if (currentBorderAdorner == null || !currentBorderAdorner.IsWarning)
								{
									if (adorner != null)
									{
										layer.Remove(adorner);
									}

									adorner = new BorderAdorner(view, Colors.Gold);

									layer.Add(adorner);
								}
								break;
							case ValidationState.Invalid:
								if (currentBorderAdorner == null || !currentBorderAdorner.IsWarning)
								{
									if (adorner != null)
									{
										layer.Remove(adorner);
									}

									adorner = new BorderAdorner(view, Colors.Red);

									layer.Add(adorner);
								}
								break;
							case ValidationState.InvalidRequired:
								if (!(adorner is ImageAdorner))
								{
									if (adorner != null)
									{
										layer.Remove(adorner);
									}

									adorner = new ImageAdorner(view, new Uri("/StyleMVVM;component/Images/Asterisk.png", UriKind.Relative));

									layer.Add(adorner);
								}
								break;
						}
					}

					if (Validation.GetAssignMessageToTooltip(view))
					{
						ToolTipService.SetToolTip(view, validationMessage);
					}
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while transitioning control to state: " + state, supplemental, exp);
				}
			}
		}

		public void Close()
		{
			validationContext.UnregisterPropertyValidationHandler(propertyName, this);
		}

		private void LoadedHandler(object sender, RoutedEventArgs args)
		{
			Control control = sender as Control;

			if (control != null)
			{
				control.Loaded -= LoadedHandler;

				ProcessPropertyValidation(
					validationContext.PropertyResults(propertyName));
			}
		}
	}
}